﻿using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Threading;

using LiveCharts;
using LiveCharts.Wpf;

using File = System.IO.File;
using Excel = Microsoft.Office.Interop.Excel;
using MessageBox = System.Windows.Forms.MessageBox;
using System.Net.Mail;
using System.Configuration;

using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Sms.V20210111;
using TencentCloud.Sms.V20210111.Models;


namespace HZWA
{
    public partial class MainForm : Form
    {

        bool debug = true;
        const int DEBUG_W = 1;//警告
        const int DEBUG_I = 2;//信息
        const int DEBUG_E = 3;//错误
        const int DEBUG_S = 4;//统计

        [DllImport("kernel32.dll")]
        public static extern Boolean AllocConsole();
        [DllImport("kernel32.dll")]
        public static extern Boolean FreeConsole();

        private void DebugWriteLine(string p, int type)
        {
            if (debug)
            {
                Console.BackgroundColor = ConsoleColor.Black;
                switch (type)
                {
                    case DEBUG_E://错误
                        Console.ForegroundColor = ConsoleColor.Red;
                        break;
                    case DEBUG_W://警告
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        break;
                    case DEBUG_S://统计
                        Console.ForegroundColor = ConsoleColor.Blue;
                        break;
                    case DEBUG_I://信息
                        Console.ForegroundColor = ConsoleColor.White;
                        break;
                }
                Console.WriteLine(p);
                Console.ForegroundColor = ConsoleColor.White;
            }
        }

        public const int RECEIVE = 1;
        public const int SEND = 2;
        //安全调用
        delegate void AddMessageToListCallback(string name, byte[] content, int type);
        delegate void timeCallback(object sender, System.Timers.ElapsedEventArgs e);
        //服务器套接字
        private static Socket serverSocket;
        //是否开启了服务
        private bool isServer = false;
        //监听线程
        private Thread receiveTh;

        private bool isSyncSet = false;

        private long countR = 0, countT = 0;

        private System.Timers.Timer timer { get; set; }//定时器，用来作为更新图标动态生成用

        private Random R { get; set; }//为了给Y值提供随机值

        //定时刷新曲线
        private System.Windows.Forms.Timer timerChart;

        private long lastTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();

        //采样频率
        private long frequency = 1;

        //采集间隔
        private long intervalTime = 1000;//(毫秒)

        //显示时长
        private long showTimes = 60;//(秒)

        //X轴值数量初始值
        private long xNumber = 60;

        private long times = 60;
        //通道初始值
        private int channelIndex = 0;
        //通道初始值
        private int rasterIndex = 0;
        //光栅总数
        private int rasterCount = 16;
        //通道总数
        private int channelCount = 16;

        private static string excelFileName = "光纤检测通道数据";

        private static string excelFileNameDate = "";

        private Excel.Application excelApp;

        private Excel._Workbook workbook;

        //通道1传感器1应变光栅初始波长
        private double C1S1A1 = 1552.638;

        //通道1传感器2应变光栅初始波长
        private double C1S2A1 = 1558.238;

        //通道3传感器1应变光栅初始波长
        private double C3S1A1 = 1543.806;

        //通道1传感器1温度光栅初始波长
        private double C1S1A2 = 1534.793;

        //通道1传感器2温度光栅初始波长
        private double C1S2A2 = 1554.753;

        //通道3传感器1温度光栅初始波长
        private double C3S1A2 = 1529.112;

        private double C1S1K1 = 0.00177;
        private double C1S1K2 = 0.0105;
        private double C1S1K3 = 0.0287;

        private double C1S2K1 = 0.00185;
        private double C1S2K2 = 0.01025;
        private double C1S2K3 = 0.0298;

        private double C3S1K1 = 0.00184;
        private double C3S1K2 = 0.01;
        private double C3S1K3 = 0.0288;
        //弹性模量(T1)
        private double EM1 = 0.068348;
        //弹性模量(T2)
        private double EM2 = 2444.0651;
        //初始轴力(F1)
        private double AF1 = 8665.69;
        //初始轴力(F2)
        private double AF2 = 1698.665046;
        //初始轴力(F2)
        private double PI = 3.1415926;
        //项目方提供
        private double PE = 322568.55;

        //轴力超过阈值次数
        private int axialAlarmRepeatTimes = 0;

        private bool isAlarmNotify = false;
        private double axialForceExceeds = 10000.0;
        private int repeatTimes = 100;

        private bool isAlarmEmail = true;
        private string alarmEmail = "";

        private bool isAlarmPhone = false;
        private string alarmPhone = "";


        public MainForm()
        {
            //this.xNumber = this.showTimes * frequency;

            //初始化界面
            InitializeComponent();

            this.isAlarmNotify = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmNotify"]);
            this.cbAlarmNotify.Checked = this.isAlarmNotify;
            
            this.axialForceExceeds = Convert.ToDouble(ConfigurationManager.AppSettings["tbAxialForceExceeds"]);
            this.tbAxialForceExceeds.Text = this.axialForceExceeds.ToString();

            this.repeatTimes = Convert.ToInt16(ConfigurationManager.AppSettings["tbRepeatTimes"]);
            this.tbRepeatTimes.Text = this.repeatTimes.ToString();

            this.isAlarmPhone  = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmPhone"]);
            this.cbAlarmPhone.Checked = this.isAlarmPhone;

            this.alarmPhone = ConfigurationManager.AppSettings["tbAlarmPhone"];
            this.tbAlarmPhone.Text = this.alarmPhone;
            
            this.isAlarmEmail = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmEmail"]);
            this.cbAlarmEmail.Checked = this.isAlarmEmail;

            this.alarmEmail = ConfigurationManager.AppSettings["tbAlarmEmail"];
            this.tbAlarmEmail.Text = this.alarmEmail;

            InitIntfaceData();

            //定时器
            this.timer = new System.Timers.Timer();
            this.timer.Enabled = true;
            //执行间隔时间,单位为毫秒; 这里实际间隔为1秒钟
            this.timer.Interval = intervalTime;
            this.timer.Start();
            this.timer.Elapsed += new System.Timers.ElapsedEventHandler(showTime);
            this.debug = this.cbSimulationTest.Checked;

            if (debug)
            {
                AllocConsole();
                DebugWriteLine("控制台已打开！\n时间：" + DateTime.Now.ToString(
                        "[yyyy-MM-dd HH:mm:ss.fff] "), DEBUG_I);
            }
            R = new Random();

            InitChartValuesData();

            InitChartConfig(this.channelIndex);

            #region  多Y轴曲线
            //将自定义类型MeasureModel映射到曲线坐标点
            var mapper = LiveCharts.Configurations.Mappers.Xy<DateTimePoint>()
              .X(model => model.Time)    //使用 Time.作为X轴的标签
              .Y(model => model.Value);  //使用Value 作为Y轴的值

            //全局保存映射器
            Charting.For<DateTimePoint>(mapper);
            #endregion

            if (this.tbFrequency.Text.Length > 0)
            {
                long _frequency = Convert.ToInt64(this.tbFrequency.Text);
                //转换成长整型
                if (this.frequency != _frequency)
                {
                    this.frequency = _frequency;
                    this.intervalTime = 1000 / this.frequency;
                    this.xNumber = this.showTimes * this.frequency;
                }
            }

            createExcelDataFile();

            //readExcelDataFile();

            //定时器刷新
            this.timerChart = new System.Windows.Forms.Timer()
            {
                Interval = (int)(this.intervalTime)
            };
            this.timerChart.Tick += TimerChart_Tick;
            this.timerChart.Start();

            btnOpenServer_Click(null, null);
            btnSendMessage_Click(null, null);
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);

            // 在这里添加你的关闭逻辑
            DialogResult result = MessageBox.Show("确定要关闭吗？", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result == DialogResult.No)
            {
                // 如果用户选择"No"，取消关闭
                e.Cancel = true;
            }
            else
            {
                excelApp.DisplayAlerts = false;
                workbook.Close(false);
                excelApp.Quit();
                workbook = null;
                excelApp = null;
                GC.Collect();//强行销毁

                if (debug)
                {
                    DebugWriteLine("Excel file close", DEBUG_E);
                }
                this.timerChart.Stop();
            }
        }

        private void showTime(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (this.lbSendState.IsDisposed)
            {
                return;
            }

            //处理InvokeRequired，安全调用
            if (this.lbSendState.InvokeRequired)
            {
                timeCallback d = new timeCallback(showTime);
                this.Invoke(d, new object[] { sender, e });
            }
            else
            {
                lbSendState.Text = DateTime.Now.ToString("时间  HH:mm:ss");
                lbSendState.Refresh();
            }
        }


        private void InitIntfaceData()
        {
            //得到主机名
            string HostName = Dns.GetHostName();
            if (debug)
            {
                DebugWriteLine("主机名：" + HostName, DEBUG_I);
            }
            //通过主机名获取IP
            IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
            //可变数组
            ArrayList addressList = new ArrayList();

            addressList.Add("0.0.0.0");
            addressList.Add("127.0.0.1");

            for (int i = 0; i < IpEntry.AddressList.Length; i++)
            {
                //从IP地址列表中筛选出IPv4类型的IP地址
                //AddressFamily.InterNetwork表示此IP为IPv4,
                //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                {
                    if (debug)
                    {
                        DebugWriteLine("IPV4:" + IpEntry.AddressList[i].ToString(), DEBUG_I);
                    }
                    addressList.Add(IpEntry.AddressList[i].ToString());
                }
            }
            //固定大小
            addressList.TrimToSize();
            object[] adList = new object[addressList.Count];
            for (int i = 0; i < addressList.Count; i++)
            {
                adList[i] = addressList[i];
            }
            this.cbMyAddress.Items.AddRange(adList);
            this.cbServerAddress.Items.AddRange(adList);
            this.cbMyAddress.SelectedIndex = 0;
            this.cbServerAddress.SelectedIndex = 0;
            this.comboBoxChannel.SelectedIndex = 0;
        }

        //给消息列表添加消息
        private void AddMessageToList(string name, byte[] content, int type)
        {
            if (this.rtbMessageList.IsDisposed)
            {
                return;
            }
            //处理InvokeRequired，安全调用
            if (this.rtbMessageList.InvokeRequired)
            {
                AddMessageToListCallback d = new AddMessageToListCallback(AddMessageToList);
                this.Invoke(d, new object[] { name, content, type });
            }
            else
            {
                this.lbSendState.Text = "产生消息";
                this.lbSendState.Refresh();
                string str = "";
                string show = "";
                int i = 0;
                if (this.cbHex.Checked)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (byte b in content)
                    {
                        //{0:X2} 大写
                        sb.AppendFormat("{0:x2}", b);
                    }
                    show = sb.ToString();
                }
                else
                {
                    show = Encoding.UTF8.GetString(content);
                }

                if (debug)
                {
                    DebugWriteLine("共" + i + "个字符", DEBUG_I);
                }

                //构建消息头
                switch (type)
                {
                    case RECEIVE:
                        this.countR += 1;
                        this.rtbMessageList.SelectionColor = System.Drawing.Color.Red;
                        str = DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss.fff] ") + " 收到：" + name + "\n";
                        break;
                    case SEND:
                        this.countT += 1;
                        this.rtbMessageList.SelectionColor = System.Drawing.Color.Orange;
                        str = DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss.fff] ") + " 发自：" + name + "\n";
                        break;
                }

                //写入消息头
                this.rtbMessageList.AppendText(str);

                //写入消息内容
                this.rtbMessageList.SelectionColor = System.Drawing.Color.Black;

                this.rtbMessageList.AppendText(show);
                this.rtbMessageList.AppendText("\r\n");

                this.lbCount.Text = "接收：" + this.countR + " 发送：" + this.countT;
                this.lbSendState.Text = "";

                if (debug)
                {
                    if (type == SEND)
                    {
                        DebugWriteLine("发送消息！发到：" + name + "    内容：" + show, DEBUG_I);
                    }
                    else
                    {
                        DebugWriteLine("收到消息！来自：" + name + "    内容：" + show, DEBUG_I);
                    }
                }
            }
        }

        //点击“开启服务按钮”
        private void btnOpenServer_Click(object sender, EventArgs e)
        {
            //停用按钮，防止多次点击
            this.btnOpenServer.Enabled = false;
            if (debug)
            {
                DebugWriteLine("停用按钮！", DEBUG_I);
            }
            //如果服务是打开的
            if (this.isServer)
            {
                this.lbSendState.Text = "关闭服务器";
                if (debug)
                {
                    DebugWriteLine("服务器处于打开状态！", DEBUG_S);
                }
                //如果套接字不为空
                if (serverSocket != null)
                {
                    //先关闭线程，再关闭服务器
                    this.receiveTh.Abort();
                    if (debug)
                    {
                        DebugWriteLine("关闭服务器！", DEBUG_I);
                    }
                    //关闭
                    serverSocket.Close();
                }

                //设置界面为服务关闭状态
                this.lbServerState.Text = "服务器已关闭";
                this.lbServerState.ForeColor = System.Drawing.Color.Blue;
                this.btnOpenServer.Text = "打开服务端口";
                this.pbServerState.BackgroundImage = global::HZWA.Properties.Resources.close;
                //允许编辑端口
                this.tbServerPort.ReadOnly = false;
                //允许编辑地址
                this.cbServerAddress.Enabled = true;
                //关闭同步
                this.cbSyncServer.Checked = false;

                //设置服务为关闭状态
                this.isServer = false;
                this.lbSendState.Text = "";
                this.cbMyAddress.Enabled = true;
                this.tbMyPort.Enabled = true;
                this.cbSyncServer.Enabled = true;
                this.tbAbverseIp.Enabled = true;
                this.tbAbversePort.Enabled = true;
                this.cbHex.Enabled = true;
                this.cbSimulationTest.Enabled = true;
                this.tbServerPort.Enabled = true;
                this.cbServerAddress.Enabled = true;
            }
            else
            {
                //如果服务是关闭的
                this.lbSendState.Text = "开启服务器";
                serverSocket = null;
                if (debug)
                {
                    DebugWriteLine("服务器处于关闭状态！", DEBUG_S);
                }
                //获取地址
                string ip = getIPAddress(cbServerAddress.Text);
                if (ip == null)
                {
                    if (debug)
                    {
                        DebugWriteLine("服务器地址不正确", DEBUG_E);
                    }
                    MessageBox.Show("服务器地址不正确，请输入IP或者域名！\n域名需要有DNS服务器！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    DebugWriteLine("启用按钮！", DEBUG_I);
                    this.btnOpenServer.Enabled = true;
                    return;
                }
                if (debug)
                {
                    DebugWriteLine("服务器地址：" + ip, DEBUG_S);
                }
                int port = getPort(tbServerPort.Text);
                if (port == -1)
                {
                    if (debug)
                    {
                        DebugWriteLine("服务器端口格式不对！", DEBUG_E);
                    }
                    MessageBox.Show("服务器端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    if (debug)
                    {
                        DebugWriteLine("启用按钮！", DEBUG_I);
                    }
                    this.btnOpenServer.Enabled = true;
                    return;
                }

                if (debug)
                {
                    DebugWriteLine("服务器端口：" + port, DEBUG_S);
                }

                try
                {
                    if (debug)
                    {
                        DebugWriteLine("服务器打开中......", DEBUG_I);
                    }
                    //创建套接字，以UDP的方式
                    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    //绑定端口号和IP
                    serverSocket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
                }
                catch (SocketException es)
                {
                    //出现异常
                    if (debug)
                    {
                        DebugWriteLine("出现异常：" + es.ErrorCode + "！", DEBUG_E);
                    }
                    MessageBox.Show("绑定端口时出现异常：" + es.ErrorCode + "！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    if (debug)
                    {
                        DebugWriteLine("启用按钮！", DEBUG_S);
                    }
                    //按钮启用
                    btnOpenServer.Enabled = true;
                }
                //如果打开了并且绑定端口了
                if (serverSocket != null && serverSocket.IsBound)
                {
                    if (debug)
                    {
                        DebugWriteLine("服务器开启成功！", DEBUG_I);
                    }

                    //UI显示
                    //设置界面为服务打开状态
                    this.lbServerState.Text = "服务器已开启";
                    this.lbServerState.ForeColor = System.Drawing.Color.Red;
                    this.btnOpenServer.Text = "关闭服务端口";
                    this.pbServerState.BackgroundImage = global::HZWA.Properties.Resources.open;
                    //不允许编辑端口
                    this.tbServerPort.ReadOnly = true;
                    //不允许编辑地址
                    this.cbServerAddress.Enabled = false;
                    //设置服务为打开状态
                    this.isServer = true;

                    this.cbMyAddress.Enabled = false;
                    this.tbMyPort.Enabled = false;
                    this.cbSyncServer.Enabled = false;
                    this.tbAbverseIp.Enabled = false;
                    this.tbAbversePort.Enabled = false;
                    this.cbHex.Enabled = false;
                    this.cbSimulationTest.Enabled = false;
                    this.tbServerPort.Enabled = false;
                    this.cbServerAddress.Enabled = false;
                    //开启接收消息线程
                    this.receiveTh = new Thread(ReciveMsg);
                    this.receiveTh.Start();

                    //打开同步
                    this.cbSyncServer.Checked = true;
                }
            }

            if (debug)
            {
                DebugWriteLine("启用按钮！", DEBUG_S);
            }
            this.btnOpenServer.Enabled = true;
            this.lbSendState.Text = "";
        }

        //发送数据报
        private void btnSendMessage_Click(object sender, EventArgs e)
        {
            if (!isServer)
            {
                MessageBox.Show("请先打开服务端口！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            this.lbSendState.Text = "发送数据报";
            //表示这个是不是服务器的套接字
            bool isUseServer = false;
            Socket clientSocket = null;
            //停用按钮，防止多次点击
            this.btnSendMessage.Enabled = false;
            if (debug)
            {
                DebugWriteLine("停用按钮！", DEBUG_S);
            }
            //如果服务是打开的
            if (this.isServer
                    && (serverSocket != null)
                    && this.cbServerAddress.Text.Equals(this.cbMyAddress.Text)
                    && this.tbServerPort.Text.Equals(tbMyPort.Text))
            { //并且服务器和发送的设置是一样的
                isUseServer = true;
                //直接使用服务器的套接字
                clientSocket = serverSocket;
            }
            else
            {//如果服务是关闭的,或者设置不同
                string myIp = getIPAddress(this.cbMyAddress.Text);//获取地址
                if (myIp == null)
                {
                    if (debug)
                    {
                        DebugWriteLine("发送端地址不正确", DEBUG_E);
                    }
                    MessageBox.Show("发送端地址不正确，请输入IP或者域名！\n域名需要有DNS服务器！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    DebugWriteLine("启用按钮！", DEBUG_I);
                    this.btnSendMessage.Enabled = true;
                    return;
                }
                if (debug)
                {
                    DebugWriteLine("发送端地址：" + myIp, DEBUG_S);
                }
                int myPort = getPort(this.tbMyPort.Text);
                if (myPort == -1)
                {
                    if (debug)
                    {
                        DebugWriteLine("发送端端口格式不对！", DEBUG_E);
                    }
                    MessageBox.Show("发送端端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    if (debug)
                    {
                        DebugWriteLine("启用按钮！", DEBUG_I);
                    }
                    this.btnSendMessage.Enabled = true;
                    return;
                }

                if (debug)
                {
                    DebugWriteLine("发送端端口：" + myPort, DEBUG_S);
                }
                //创建套接字
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                //绑定端口号和IP
                clientSocket.Bind(new IPEndPoint(IPAddress.Parse(myIp), myPort));
            }
            //获取地址
            string adIp = getIPAddress(this.tbAbverseIp.Text);
            if (adIp == null)
            {
                if (debug)
                {
                    DebugWriteLine("对方主机地址不正确", DEBUG_E);
                }
                MessageBox.Show("对方主机地址不正确，请输入IP或者域名！\n域名需要有DNS服务器！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                if (debug)
                {
                    DebugWriteLine("启用按钮！", DEBUG_I);
                }
                this.btnSendMessage.Enabled = true;
                return;
            }
            if (debug)
            {
                DebugWriteLine("对方主机地址：" + adIp, DEBUG_S);
            }
            //对方端口
            int adPort = getPort(this.tbAbversePort.Text);
            if (adPort == -1)
            {
                if (debug)
                {
                    DebugWriteLine("对方主机端口格式不对！", DEBUG_E);
                }
                MessageBox.Show("对方主机端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                if (debug)
                {
                    DebugWriteLine("启用按钮！", DEBUG_I);
                }
                this.btnSendMessage.Enabled = true;
                return;
            }

            if (debug)
            {
                DebugWriteLine("接收端端口：" + adPort, DEBUG_S);
            }
            //设置好地址
            EndPoint adversePoint = new IPEndPoint(IPAddress.Parse(adIp), adPort);
            try
            {
                string msg = this.btnSendMessage.Text;
                if ((!string.IsNullOrEmpty(msg)) && ("开始采集".Equals(msg)))
                {
                    string _frequency = this.tbFrequency.Text;
                    if (_frequency != null && _frequency.Length > 0)
                    {
                        //转换成长整型
                        if (this.frequency != Convert.ToInt64(_frequency))
                        {
                            this.frequency = Convert.ToInt64(_frequency);
                            this.intervalTime = 1000 / this.frequency;
                            this.xNumber = this.showTimes * this.frequency;
                        }
                    }
                    //如果不为空
                    //byte[] bmsg = Encoding.UTF8.GetBytes(msg);
                    byte[] bmsg = { 0x01, 0x0A, 0x55 };
                    if (bmsg != null)
                    {
                        //发送数据包
                        clientSocket.SendTo(bmsg, adversePoint);
                        //UI显示
                        AddMessageToList(adversePoint.ToString(), bmsg, SEND);
                        this.btnSendMessage.Text = "停止采集";
                        this.tbFrequency.Enabled = false;
                        this.btnResetFrequency.Enabled = false;
                        this.btnClearInput.Enabled = false;
                        this.tbElasticModulus1.Enabled = false;
                        this.tbElasticModulus2.Enabled = false;
                        this.tbAxialForce1.Enabled = false;
                        this.tbAxialForce2.Enabled = false;
                    }
                    else
                    {
                        if (debug)
                        {
                            DebugWriteLine("发送数据格式错误：" + msg, DEBUG_E);
                        }
                        MessageBox.Show("发送数据格式错误：" + msg, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
                else
                {
                    byte[] bmsg = { 0x01, 0x0A, 0x00 };
                    if (bmsg != null)
                    {
                        //发送数据包
                        clientSocket.SendTo(bmsg, adversePoint);
                        //UI显示
                        AddMessageToList(adversePoint.ToString(), bmsg, SEND);
                        this.btnSendMessage.Text = "开始采集";
                        this.tbFrequency.Enabled = true;
                        this.btnResetFrequency.Enabled = true;
                        this.btnClearInput.Enabled = true;
                        this.tbElasticModulus1.Enabled = true;
                        this.tbElasticModulus2.Enabled = true;
                        this.tbAxialForce1.Enabled = true;
                        this.tbAxialForce2.Enabled = true;
                    }
                    else
                    {
                        if (debug)
                        {
                            DebugWriteLine("发送数据格式错误：" + msg, DEBUG_E);
                        }
                        MessageBox.Show("发送数据格式错误：" + msg, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
            catch (SocketException es)
            {
                if (debug)
                {
                    DebugWriteLine("出现异常：" + es.ErrorCode + "！", DEBUG_E);
                }
                MessageBox.Show("发送数据报时出现异常：" + es.ErrorCode + "！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            this.btnSendMessage.Enabled = true;
            this.lbSendState.Text = "";
            //如果是新开的套接字
            if (!isUseServer)
            {
                if (clientSocket != null)
                {
                    //用完之后关掉
                    clientSocket.Close();
                }
            }
            if (debug)
            {
                DebugWriteLine("启用按钮！", DEBUG_S);
            }

        }

        //重新设置扫描频率
        private void btnResetFrequency_Click(object sender, EventArgs e)
        {
            if (!isServer)
            {
                MessageBox.Show("请先打开服务端口！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            this.lbSendState.Text = "发送数据报";
            //表示这个是不是服务器的套接字
            bool isUseServer = false;
            Socket clientSocket = null;
            //停用按钮，防止多次点击
            this.tbFrequency.Enabled = false;
            this.btnResetFrequency.Enabled = false;
            if (debug)
            {
                DebugWriteLine("重置按钮！", DEBUG_S);
            }
            //如果服务是打开的
            if (this.isServer
                    && (serverSocket != null)
                    && this.cbServerAddress.Text.Equals(this.cbMyAddress.Text)
                    && this.tbServerPort.Text.Equals(tbMyPort.Text))
            { //并且服务器和发送的设置是一样的
                isUseServer = true;
                //直接使用服务器的套接字
                clientSocket = serverSocket;
            }
            else
            {//如果服务是关闭的,或者设置不同
                string myIp = getIPAddress(this.cbMyAddress.Text);//获取地址
                if (myIp == null)
                {
                    this.tbFrequency.Enabled = true;
                    this.btnResetFrequency.Enabled = true;
                    if (debug)
                    {
                        DebugWriteLine("发送端地址不正确", DEBUG_E);
                        DebugWriteLine("启用按钮！", DEBUG_I);
                    }
                    MessageBox.Show("发送端地址不正确，请输入IP或者域名！\n域名需要有DNS服务器！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                if (debug)
                {
                    DebugWriteLine("发送端地址：" + myIp, DEBUG_S);
                }
                int myPort = getPort(this.tbMyPort.Text);
                if (myPort == -1)
                {
                    this.tbFrequency.Enabled = true;
                    this.btnResetFrequency.Enabled = true;
                    if (debug)
                    {
                        DebugWriteLine("发送端端口格式不对！", DEBUG_E);
                        DebugWriteLine("重置按钮！", DEBUG_I);
                    }
                    MessageBox.Show("发送端端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                if (debug)
                {
                    DebugWriteLine("发送端端口：" + myPort, DEBUG_S);
                }
                //创建套接字
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                //绑定端口号和IP
                clientSocket.Bind(new IPEndPoint(IPAddress.Parse(myIp), myPort));
            }
            //获取地址
            string adIp = getIPAddress(this.tbAbverseIp.Text);
            if (adIp == null)
            {
                this.tbFrequency.Enabled = true;
                this.btnResetFrequency.Enabled = true;
                if (debug)
                {
                    DebugWriteLine("对方主机地址不正确", DEBUG_E);
                    DebugWriteLine("重置按钮！", DEBUG_I);
                }
                MessageBox.Show("对方主机地址不正确，请输入IP或者域名！\n域名需要有DNS服务器！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (debug)
            {
                DebugWriteLine("对方主机地址：" + adIp, DEBUG_S);
            }
            //对方端口
            int adPort = getPort(this.tbAbversePort.Text);
            if (adPort == -1)
            {
                this.tbFrequency.Enabled = true;
                this.btnResetFrequency.Enabled = true;
                if (debug)
                {
                    DebugWriteLine("对方主机端口格式不对！", DEBUG_E);
                    DebugWriteLine("重置按钮！", DEBUG_I);
                }
                MessageBox.Show("对方主机端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (debug)
            {
                DebugWriteLine("接收端端口：" + adPort, DEBUG_S);
            }
            //设置好地址
            EndPoint adversePoint = new IPEndPoint(IPAddress.Parse(adIp), adPort);
            try
            {
                int frequency = Convert.ToInt16(this.tbFrequency.Text);

                //如果不为空
                byte[] bmsg = { 0x01, 0x2E, 0x01, 0x00 };
                if (bmsg != null)
                {
                    //发送数据包
                    clientSocket.SendTo(bmsg, adversePoint);
                    //UI显示
                    AddMessageToList(adversePoint.ToString(), bmsg, SEND);
                    this.tbFrequency.Enabled = true;
                    this.btnResetFrequency.Enabled = true;
                }
                else
                {
                    if (debug)
                    {
                        DebugWriteLine("发送数据格式错误：" + bmsg, DEBUG_E);
                    }
                    MessageBox.Show("发送数据格式错误：" + bmsg, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (SocketException es)
            {
                if (debug)
                {
                    DebugWriteLine("出现异常：" + es.ErrorCode + "！", DEBUG_E);
                }
                MessageBox.Show("发送数据报时出现异常：" + es.ErrorCode + "！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            this.tbFrequency.Enabled = true;
            this.btnResetFrequency.Enabled = true;
            this.lbSendState.Text = "";

            //如果是新开的套接字
            if (!isUseServer)
            {
                if (clientSocket != null)
                {
                    //用完之后关掉
                    clientSocket.Close();
                }
            }
            if (debug)
            {
                DebugWriteLine("重置按钮！", DEBUG_S);
            }
        }


        // 接收发送给本机IP对应端口号的数据报
        private void ReciveMsg()
        {
            try
            {
                while (this.isServer)
                {
                    //用来保存发送方的IP和端口号
                    EndPoint point = new IPEndPoint(IPAddress.Any, 0);
                    //消息缓存区申请
                    byte[] buffer = new byte[512];
                    //接收数据报
                    int length = serverSocket.ReceiveFrom(buffer, ref point);
                    if (debug)
                    {
                        DebugWriteLine("接收数据长度 length：" + length, DEBUG_I);
                    }
                    byte[] revBuf = new byte[length];
                    Buffer.BlockCopy(buffer, 0, revBuf, 0, length);
                    //显示在消息列表里
                    //AddMessageToList(point.ToString(), revBuf, RECEIVE);
                    //显示在波形图里
                    this.refreshData(revBuf);
                }
            }
            catch (SocketException e)
            {
                DebugWriteLine(e.Message, DEBUG_E);
            }
        }


        //窗口关闭事件委托，在窗口关闭事件前处理
        private void UdpForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.isServer = false;
            if (this.receiveTh != null)
            {
                if (debug)
                {
                    DebugWriteLine("关闭接收线程！", DEBUG_I);
                }
                this.receiveTh.Abort();
            }
            if (serverSocket != null)
            {
                if (debug)
                {
                    DebugWriteLine("关闭服务器！", DEBUG_I);
                }
                serverSocket.Close();
            }
            if (debug)
            {
                DebugWriteLine("关闭窗口！", DEBUG_I);
            }
        }

        //清空消息列表
        private void btnClearList_Click(object sender, EventArgs e)
        {
            if (debug)
            {
                DebugWriteLine("清空消息列表！", DEBUG_I);
            }
            this.rtbMessageList.Text = "";
        }

        //清空输入框
        private void btnClearInput_Click(object sender, EventArgs e)
        {
            if (debug)
            {
                DebugWriteLine("清空输入框！", DEBUG_I);
            }
            this.tbFrequency.Text = "1";
        }

        //根据字符串获取端口
        private int getPort(string str)
        {
            if (!IsPort(str))
            {
                //不是端口
                return -1;
            }
            //转换成整型
            int port = Convert.ToInt32(str);
            if (port >= 0 && port <= 65535)
            {
                //如果是合法端口
                return port;
            }
            else
            {//不是合法端口
                return -1;
            }
        }

        //根据字符串获取IP地址
        string getIPAddress(string str)
        {
            if (!IsIP(str))
            {
                //如果不是IP
                if (IsDomain(str))
                {
                    //如果是域名
                    try
                    {
                        //解析域名
                        IPAddress[] host = Dns.GetHostAddresses(str);
                        //域名解析的IP地址
                        IPAddress ipAddress = host[0];
                        //改成IP地址
                        str = ipAddress.ToString();
                    }
                    catch
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
            }
            //返回IP地址
            return str;
        }

        // 验证字符串是否是域名
        public bool IsDomain(string str)
        {
            string pattern = @"^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$";
            return IsMatch(pattern, str);
        }

        // 验证字符串是否是IP
        bool IsIP(string str)
        {
            string pattern = @"(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)";
            return IsMatch(pattern, str);
        }

        //判断是否为0-65535的数字
        bool IsPort(string str)
        {
            string pattern = @"^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$";
            return IsMatch(pattern, str);
        }

        // 判断一个字符串，是否匹配指定的表达式(区分大小写的情况下)
        public bool IsMatch(string expression, string str)
        {
            Regex reg = new Regex(expression);
            if (string.IsNullOrEmpty(str))
            {
                return false;
            }
            return reg.IsMatch(str);
        }

        //勾选与取消勾选“与服务器同步设置”
        private void cbSyncServer_CheckedChanged(object sender, EventArgs e)
        {
            System.Windows.Forms.CheckBox cb = (System.Windows.Forms.CheckBox)sender;
            if (cb.Equals(this.cbSyncServer))
            {
                if (cb.Checked)
                {
                    //同步设置
                    this.isSyncSet = true;
                    this.cbMyAddress.Enabled = false;
                    this.tbMyPort.ReadOnly = true;
                    //复制服务器的地址
                    this.cbMyAddress.Text = cbServerAddress.Text;
                    //复制服务器的端口
                    this.tbMyPort.Text = tbServerPort.Text;
                }
                else
                {
                    //不同步设置
                    this.isSyncSet = false;
                    this.cbMyAddress.Enabled = true;
                    this.tbMyPort.ReadOnly = false;
                }
            }
        }

        //同步地址事件
        private void cbServerAddress_TextChanged(object sender, EventArgs e)
        {
            if (this.isSyncSet)
            {
                //复制服务器的地址
                this.cbMyAddress.Text = cbServerAddress.Text;
            }
        }

        //同步端口事件
        private void tbServerPort_TextChanged(object sender, EventArgs e)
        {
            if (this.isSyncSet)
            {
                //复制服务器的端口
                this.tbMyPort.Text = tbServerPort.Text;
            }
        }

        /// <summary>
        /// 实现自动下拉
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rtbMessageList_TextChanged(object sender, EventArgs e)
        {
            this.rtbMessageList.SelectionStart = rtbMessageList.Text.Length;
            this.rtbMessageList.ScrollToCaret();
        }

        private void lbHelp_Click(object sender, EventArgs e)
        {
            MessageBox.Show("这个程序是一个UDP收发器，能接收和发送UDP报文。\nUDP广播地址：***.***.***.255\n2020-4 Minuy.top 网络编程作业\n开源地址：https://gitee.com/minuy/udp-transceiver", "帮助", MessageBoxButtons.OK);
        }

        private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
        {
            MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
        }

        private ChartValues<DateTimePoint> InitChartValuesData()
        {
            ChartValues<DateTimePoint> chartValues = new ChartValues<DateTimePoint>();
            for (long i = 1; i <= this.xNumber; i++)
            {
                chartValues.Add(new DateTimePoint(i, 9200.00001D));
            }
            this.times = this.xNumber;
            return chartValues;
        }

        private ChartValues<DateTimePoint> InitChartValuesEmptyData()
        {
            ChartValues<DateTimePoint> chartValues = new ChartValues<DateTimePoint>();
            return chartValues;
        }

        private void InitChartConfig(int channelID)
        {
            string title = "通道-" + (channelID + 1);
            string wave = "轴力值" + (channelID + 1);
            OscChart.Series.Add(new LineSeries
            {
                Title = title,
                PointGeometry = DefaultGeometries.Circle,
                PointGeometrySize = 15,
                StrokeThickness = 4,
                LineSmoothness = 0,
                PointForeground = System.Windows.Media.Brushes.Blue,
                Values = InitChartValuesData(),
                ScalesYAt = 0
            });
            OscChart.AxisX.Add(new LiveCharts.Wpf.Axis
            {
                //Title = "时间",
                FontSize = 12,
                DisableAnimations = true,
                Foreground = System.Windows.Media.Brushes.Black,
                LabelFormatter = value => value.ToString(),
                //LabelFormatter = value => new System.DateTime(System.DateTime.Now.Ticks - (long)(this.xNumber - (long)value) * this.intervalTime * 10000).ToString("mm:ss"),
                Separator = new Separator
                {
                    Step = 1,
                    IsEnabled = true
                },
                MaxValue = this.times,
                MinValue = this.times - this.xNumber
            });

            OscChart.AxisY.Add(new LiveCharts.Wpf.Axis
            {
                Title = "轴力值",
                FontSize = 12,
                Foreground = System.Windows.Media.Brushes.DodgerBlue,
                Position = AxisPosition.LeftBottom
            });
        }

        private void TimerChart_Tick(object sender, EventArgs e)
        {
            if ((debug) && (!this.isServer))
            {
                LineSeriesRefresh();
            }
        }

        /// <summary>
        /// 连续折现图的方法
        /// </summary>
        private void LineSeriesRefresh()
        {
            this.times++;

            string[] values = new string[rasterCount + 2];
            
            for (int channelID = 0; channelID < this.channelCount; channelID++)
            {
                if (channelID == this.channelIndex)
                {
                    values[0] = 1 + channelID + "";
                    values[1] = 2 + "";
                    //更新纵坐标数据
                    double waveLength1 = R.NextDouble() + this.C1S1A1;
                    double waveLength2 = R.NextDouble() + this.C1S1A2;
                    double stressValue1 = calculateStress(waveLength1, waveLength2, this.C1S1A1, this.C1S1A2, this.C1S1K1, this.C1S1K2, this.C1S1K3);

                    values[2] = waveLength1 + "";
                    values[3] = waveLength2 + "";
                    values[4] = stressValue1 + "";

                    double waveLength3 = R.NextDouble() + this.C1S2A1;
                    double waveLength4 = R.NextDouble() + this.C1S2A2;
                    double stressValue2 = calculateStress(waveLength3, waveLength4, this.C1S2A1, this.C1S2A2, this.C1S2K1, this.C1S2K2, this.C1S2K3);
                    values[5] = waveLength3 + "";
                    values[6] = waveLength4 + "";
                    values[7] = stressValue2 + "";

                    double axialForce = Math.Abs(((stressValue1 + stressValue2) * this.EM1 * this.PE) / (this.PI * 32 * 32)) + this.AF1;

                    if (axialForce > this.axialForceExceeds)
                    {
                        this.axialAlarmRepeatTimes++;
                        if (this.isAlarmNotify)
                        {
                            if (this.axialAlarmRepeatTimes > this.repeatTimes)
                            {
                                if (this.isAlarmEmail)
                                {
                                    SendEmail("smtp.qq.com", 587, "24139297@qq.com", "rvsdsfqwexwrbgba", this.alarmEmail, "告警通知", "通道：" + channelID + " 轴力值：" + axialForce + " 连续 " + this.axialAlarmRepeatTimes + " 次" + "超过警告阈值：" + this.axialForceExceeds + "，请注意！");
                                }
                                if (this.isAlarmPhone)
                                {
                                    int roundedValue = Convert.ToInt32(axialForce);
                                    string[] templateParamSet = new string[] { channelID + "", roundedValue + "", this.axialAlarmRepeatTimes + "" };
                                    SendSms(this.alarmPhone, templateParamSet);
                                }
                                this.axialAlarmRepeatTimes = 0;
                            }
                        }
                    }

                    values[8] = axialForce + "";

                    OscChart.Series[0].Values.Add(new DateTimePoint(this.times, axialForce));
                    int _count = OscChart.Series[0].Values.Count;
                    if (_count > this.xNumber)
                    {
                        //只关心前面30个数据
                        OscChart.Series[0].Values.RemoveAt(0);
                    }
                    // 假设你在非UI线程上
                    if (OscChart.InvokeRequired)
                    {
                        OscChart.Invoke(new System.Action(() =>
                        {
                            //在这里进行UI相关的操作，例如修改OscChart的属性等
                            OscChart.AxisX[0].MaxValue = this.times;
                            OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                            OscChart.Refresh();
                        }));
                    }
                    else
                    {
                        // 直接进行操作，因为当前线程就是UI线程
                        OscChart.AxisX[0].MaxValue = this.times;
                        OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                        OscChart.Refresh();
                    }
                    saveDataToExcel(values);
                }
            }
        }

        private void refreshData(byte[] revData)
        {

            long nowTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            if ((nowTime - this.lastTime) < this.intervalTime)
            {
                return;
            }
            this.lastTime = nowTime;
            this.times++;
            byte head = revData[0];
            byte cmd = revData[1];
            
            if (cmd == 0x02)
            {

            }
            else if (cmd == 0x04)
            {

            }
            else if (cmd == 0x06)
            {

            }
            else if (cmd == 0x07)
            {

            }
            else if (cmd == 0x08)
            {

            }
            else if (cmd == 0x0A)
            {

            }
            else if (cmd == 0x0C)
            {
                int index = 2;

                for (int channelID = 0; channelID < 16; channelID++)
                {
                    string[] values = new string[rasterCount + 2];
                    index = index + 1;
                    byte raster = revData[index];//4
                    values[0] = channelID + 1 + "";
                    values[1] = raster / 2 + "";
                    if (raster == 4)
                    {
                        byte[] tempArray = new byte[2 * raster];
                        Array.Copy(revData, index + 1, tempArray, 0, 2 * raster);
                        index = index + 1 + 2 * raster;
                        byte[] tempArray1 = new byte[2];
                        Array.Copy(tempArray, 0, tempArray1, 0, 2);
                        uint valueL1 = tempArray1[0];
                        uint valueH1 = tempArray1[1];
                        double waveLength1 = (double)DivideAndRound((valueL1 + valueH1 * 256), 1000, 5) + 1520;

                        byte[] tempArray2 = new byte[2];
                        Array.Copy(tempArray, 2, tempArray2, 0, 2);
                        uint valueL2 = tempArray2[0];
                        uint valueH2 = tempArray2[1];
                        double waveLength2 = (double)DivideAndRound((valueL2 + valueH2 * 256), 1000, 5) + 1520;

                        double stressValue1 = calculateStress(waveLength1, waveLength2, this.C1S1A1, this.C1S1A2, this.C1S1K1, this.C1S1K2, this.C1S1K3);

                        values[2] = waveLength1 + "";
                        values[3] = waveLength2 + "";
                        values[4] = stressValue1 + "";

                        byte[] tempArray3 = new byte[2];
                        Array.Copy(tempArray, 4, tempArray3, 0, 2);
                        uint valueL3 = tempArray3[0];
                        uint valueH3 = tempArray3[1];
                        double waveLength3 = (double)DivideAndRound((valueL3 + valueH3 * 256), 1000, 5) + 1520;

                        byte[] tempArray4 = new byte[2];
                        Array.Copy(tempArray, 6, tempArray4, 0, 2);
                        uint valueL4 = tempArray4[0];
                        uint valueH4 = tempArray4[1];
                        double waveLength4 = (double)DivideAndRound((valueL4 + valueH4 * 256), 1000, 5) + 1520;

                        double stressValue2 = calculateStress(waveLength3, waveLength4, this.C1S2A1, this.C1S2A2, this.C1S2K1, this.C1S2K2, this.C1S2K3);

                        double axialForce = Math.Abs(((stressValue1 + stressValue2) * this.EM1 * this.PE) / (this.PI * 32 * 32)) + this.AF1;

                        values[5] = waveLength3 + "";
                        values[6] = waveLength4 + "";
                        values[7] = stressValue2 + "";
                        values[8] = axialForce + "";

                        if (axialForce > this.axialForceExceeds)
                        {
                            this.axialAlarmRepeatTimes++;
                            if (this.isAlarmNotify)
                            {
                                if (this.axialAlarmRepeatTimes > this.repeatTimes)
                                {
                                    if (this.isAlarmEmail)
                                    {
                                        SendEmail("smtp.qq.com", 587, "24139297@qq.com", "rvsdsfqwexwrbgba", this.alarmEmail, "告警通知", "通道：" + channelID + " 轴力值：" + axialForce + " 连续 " + this.axialAlarmRepeatTimes + " 次" + "超过警告阈值：" + this.axialForceExceeds + "，请注意！");
                                    }
                                    if (this.isAlarmPhone)
                                    {
                                        int roundedValue = Convert.ToInt32(axialForce);
                                        string[] templateParamSet = new string[] { channelID + "", roundedValue + "", this.axialAlarmRepeatTimes + "" };
                                        SendSms(this.alarmPhone, templateParamSet);
                                    }
                                    this.axialAlarmRepeatTimes = 0;
                                }
                            }
                        }

                        if (channelID == this.channelIndex)
                        {
                            //添加y值，pointValue为波长数据
                            var pointValue = new DateTimePoint(this.times, axialForce);
                            //更新纵坐标数据
                            OscChart.Series[0].Values.Add(pointValue);
                            int _count = OscChart.Series[0].Values.Count;
                            if (_count > this.xNumber)
                            {
                                //只关心前面30个数据
                                OscChart.Series[0].Values.RemoveAt(0);
                            }
                            // 假设你在非UI线程上
                            if (OscChart.InvokeRequired)
                            {
                                OscChart.Invoke(new System.Action(() =>
                                {
                                    //在这里进行UI相关的操作，例如修改OscChart的属性等
                                    OscChart.AxisX[0].MaxValue = this.times;
                                    OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                                    OscChart.Refresh();
                                }));
                            }
                            else
                            {
                                // 直接进行操作，因为当前线程就是UI线程
                                OscChart.AxisX[0].MaxValue = this.times;
                                OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                                OscChart.Refresh();
                            }
                        }

                        //保存到Excel文件
                        saveDataToExcel(values);
                    }
                    else if (raster == 2)
                    {
                        byte[] tempArray = new byte[2 * raster];
                        Array.Copy(revData, index + 1, tempArray, 0, 2 * raster);
                        index = index + 1 + 2 * raster;
                        byte[] tempArray1 = new byte[2];
                        Array.Copy(tempArray, 0, tempArray1, 0, 2);
                        uint valueL1 = tempArray1[0];
                        uint valueH1 = tempArray1[1];
                        double waveLength1 = (double)DivideAndRound((valueL1 + valueH1 * 256), 1000, 5) + 1520;

                        byte[] tempArray2 = new byte[2];
                        Array.Copy(tempArray, 2, tempArray2, 0, 2);
                        uint valueL2 = tempArray2[0];
                        uint valueH2 = tempArray2[1];
                        double waveLength2 = (double)DivideAndRound((valueL2 + valueH2 * 256), 1000, 5) + 1520;

                        double stressValue = calculateStress(waveLength1, waveLength2, this.C3S1A1, this.C3S1A2, this.C3S1K1, this.C3S1K2, this.C3S1K3);

                        double axialForce = (this.EM2 * Math.Abs(stressValue)) * 0.00307 + this.AF2;
                        values[2] = waveLength1 + "";
                        values[3] = waveLength2 + "";
                        values[4] = stressValue + "";
                        values[8] = axialForce + "";

                        if (axialForce > this.axialForceExceeds)
                        {
                            this.axialAlarmRepeatTimes++;
                            if (this.isAlarmNotify)
                            {
                                if (this.axialAlarmRepeatTimes > this.repeatTimes)
                                {
                                    if (this.isAlarmEmail)
                                    {
                                        SendEmail("smtp.qq.com", 587, "24139297@qq.com", "rvsdsfqwexwrbgba", this.alarmEmail, "告警通知", "通道：" + channelID + " 轴力值：" + axialForce + " 连续 " + this.axialAlarmRepeatTimes + " 次" + "超过警告阈值：" + this.axialForceExceeds + "，请注意！");
                                    }
                                    if (this.isAlarmPhone)
                                    {
                                        int roundedValue = Convert.ToInt32(axialForce);
                                        string[] templateParamSet = new string[] { channelID + "", roundedValue + "", this.axialAlarmRepeatTimes + "" };
                                        SendSms(this.alarmPhone, templateParamSet);
                                    }
                                    this.axialAlarmRepeatTimes = 0;
                                }
                            }
                        }

                        if ( channelID == this.channelIndex)
                        {
                            //添加y值，pointValue为波长数据
                            var pointValue = new DateTimePoint(this.times, axialForce);
                            //更新纵坐标数据
                            OscChart.Series[0].Values.Add(pointValue);
                            int _count = OscChart.Series[0].Values.Count;
                            if (_count > this.xNumber)
                            {
                                //只关心前面30个数据
                                OscChart.Series[0].Values.RemoveAt(0);
                            }
                            // 假设你在非UI线程上
                            if (OscChart.InvokeRequired)
                            {
                                OscChart.Invoke(new System.Action(() =>
                                {
                                    //在这里进行UI相关的操作，例如修改OscChart的属性等
                                    OscChart.AxisX[0].MaxValue = this.times;
                                    OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                                    OscChart.Refresh();
                                }));
                            }
                            else
                            {
                                // 直接进行操作，因为当前线程就是UI线程
                                OscChart.AxisX[0].MaxValue = this.times;
                                OscChart.AxisX[0].MinValue = this.times - this.xNumber;
                                OscChart.Refresh();
                            }
                        }
                        //保存到Excel文件
                        saveDataToExcel(values);
                    }
                    else if (raster == 1)
                    {
                        byte[] tempArray = new byte[2 * raster];
                        Array.Copy(revData, index + 1, tempArray, 0, 2 * raster);
                        index = index + 1 + 2 * raster;
                        byte[] tempArray1 = new byte[2];
                        Array.Copy(tempArray, 0, tempArray1, 0, 2);
                        uint valueL1 = tempArray1[0];
                        uint valueH1 = tempArray1[1];
                        double waveLength1 = (double)DivideAndRound((valueL1 + valueH1 * 256), 1000, 5) + 1520;
                        values[2] = waveLength1 + "";
                        //保存到Excel文件
                        saveDataToExcel(values);
                    }
                    else
                    {
                        index = index + 1;
                    }
                    
                }
            }
            else if (cmd == 0x14)
            {

            }
            else if (cmd == 0x16)
            {

            }
            else if (cmd == 0x17)
            {

            }
            else if (cmd == 0x24)
            {

            }
            else if (cmd == 0x26)
            {

            }
            else if (cmd == 0x2E)
            {

            }
            else if (cmd == 0x30)
            {

            }
            else if (cmd == 0x32)
            {

            }
            else if (cmd == 0x34)
            {

            }

            if (cmd != 0x0C)
            {
                AddMessageToList("RECEIVE", revData, RECEIVE);
            }
        }

        //应变计算公式：ε＝[（A1’－A1）- ((A2’－A2)/ K2) * K3] /K1
        //其中：A1’为应变光栅当前测量波长，A1为应变光栅安装完成后初始波长
        // A2’为温度光栅当前测量波长，A2为温度光栅安装完成后初始波长
        // K1为应变系数， K2为温度光栅温度系数，K3为应变光栅温度系数
        private double calculateStress(double waveValue1, double waveValue2, double A1, double A2, double K1, double K2, double K3)
        {
            double stress = ((waveValue2 - A1) - ((waveValue1 - A2)/K2) * K3)/ K1;

            return stress;
        }


        private void createExcelDataFile()
        {
            // 创建Excel应用程序实例
            excelApp = new Excel.Application();
            if (excelApp == null)
            {
                MessageBox.Show("无法创建excel对象，请确认excel环境是否正常");
                return;

            }
            excelApp.Visible = false;
            excelApp.UserControl = true;
            excelApp.DisplayAlerts = false;

            DateTime dt = DateTime.Now;
            int hour = dt.Hour;
            int min = dt.Minute;
            int hh = hour * 60 + min / 1;
            string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2");
            string filePath = AppDomain.CurrentDomain.BaseDirectory + excelFileName + date + @".xlsx";
            Excel.Workbooks workbooks = excelApp.Workbooks;
            try { 
                if (!File.Exists(filePath))
                {
                    File.Copy(AppDomain.CurrentDomain.BaseDirectory + excelFileName + @".xlsx", filePath, true);
                }
                workbook = workbooks.Add(filePath); //加载模板
            }
            catch (Exception ex) {
                DebugWriteLine("Excel file created field, error:"+ ex.ToString(), DEBUG_E);
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    workbook = workbooks.Add(filePath); //加载模板
                }else{
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    return;
                }
            }
            excelFileNameDate = date;
            if (debug)
            {
                DebugWriteLine("Excel file created", DEBUG_E);
            }
        }

        private void readExcelDataFile()
        {
            // 创建Excel应用程序实例
            excelApp = new Excel.Application();
            if (excelApp == null)
            {
                MessageBox.Show("无法创建excel对象，请确认excel环境是否正常");
                return;

            }
            excelApp.Visible = false;
            excelApp.UserControl = true;
            excelApp.DisplayAlerts = false;

            DateTime dt = DateTime.Today; // 获取今天的日期
            DateTime yesterday = dt.AddDays(-1); // 获取前一天的日期

            string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2");
            string filePath = AppDomain.CurrentDomain.BaseDirectory + excelFileName + date + @".xlsx";
            Excel.Workbooks workbooks = excelApp.Workbooks;
            try
            {
                if (File.Exists(filePath))
                {
                    workbook = workbooks.Add(filePath); //加载模板
                }else{
                    File.Copy(AppDomain.CurrentDomain.BaseDirectory + excelFileName + @".xlsx", filePath, true);
                    workbook = workbooks.Add(filePath); //加载模板
                }
            }
            catch (Exception ex)
            {
                DebugWriteLine("Excel file created field, error:" + ex.ToString(), DEBUG_E);
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    workbook = workbooks.Add(filePath); //加载模板
                }
                else
                {
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    return;
                }
            }
            if (debug)
            {
                DebugWriteLine("Excel file is opened", DEBUG_E);
            }
        }

        //数据保存到excel
        public void saveDataToExcel(string[] data)
        {
            if (excelApp == null)
            {
                MessageBox.Show("无法创建 Excel 对象，请确认 Excel 环境是否正常");
                return;

            }
            if (workbook == null)
            {
                MessageBox.Show("无法打开 Workbook 对象，请确认 Excel 文件是否正常");
                return;

            }
            DateTime dt = DateTime.Now;
            int hour = dt.Hour;
            int min = dt.Minute;
            int sec = dt.Second;
            int msec = dt.Millisecond;
            int hh = hour * 60 + min / 1;
            string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2");
            string filePath = AppDomain.CurrentDomain.BaseDirectory + excelFileName + date + @".xlsx";
            if (!excelFileNameDate.Equals(date))
            {
                workbook.Close(false);
                workbook = null;
                //excelApp.Quit();
                //excelApp = null;
                Excel.Workbooks workbooks = excelApp.Workbooks;
                if (!File.Exists(filePath))
                {
                    File.Copy(AppDomain.CurrentDomain.BaseDirectory + excelFileName + @".xlsx", filePath, true);
                }
                workbook = workbooks.Add(filePath); //加载模板
                excelFileNameDate = date;
            }

            Excel.Sheets sheets = workbook.Sheets;
            Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1); //第一个工作薄。
            if (worksheet == null)
            {
                MessageBox.Show("无法打开 Worksheet 对象，请确认 Excel 文件是否正常");
                return; //工作薄中没有工作表.
            }
            worksheet.Name = date;

            int columnIndex = 1; // A列的索引是1
            int columnCount = countColumnValues(worksheet, columnIndex);
            //写入标题
            worksheet.Cells[columnCount + 1, 1] = hour.ToString() + ":" + min.ToString() + ":" + sec.ToString() + ":" + msec.ToString();

            //写入数值
            for (int i = 0; i < data.Length; i++)
            {
                worksheet.Cells[columnCount + 1, i + 2] = data[i];
            }
            worksheet.Columns.EntireColumn.AutoFit();//列宽自适应

            try
            {
                workbook.Saved = true;
                workbook.SaveAs(filePath);
            }
            catch (Exception ex)
            {
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    workbook.SaveAs(filePath);
                }else{
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    return;
                }
            }
        }

        private int countColumnValues(Excel.Worksheet worksheet, int columnIndex)
        {
            int valueCount = 0;
            Excel.Range usedRange = worksheet.UsedRange;

            // 获取列的范围
            Excel.Range columnRange = (Excel.Range)worksheet.Columns[columnIndex];
            int lastRow = columnRange.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell).Row;

            // 遍历列中的所有单元格
            for (int row = 1; row <= lastRow; row++)
            {
                Excel.Range cell = (Excel.Range)worksheet.Cells[row, columnIndex];
                if (cell.Value2 != null && !cell.Value2.ToString().Trim().Equals(""))
                {
                    valueCount++;
                }
            }

            System.Runtime.InteropServices.Marshal.ReleaseComObject(columnRange);
            columnRange = null;
            System.Runtime.InteropServices.Marshal.ReleaseComObject(usedRange);
            usedRange = null;

            return valueCount;
        }


        private void cbSimulationTest_CheckedChanged(object sender, EventArgs e)
        {
            this.debug = this.cbSimulationTest.Checked;
        }

        private void comboBoxChannel_SelectedIndexChanged(object sender, EventArgs e)
        {

            // 假设你在非UI线程上
            if (this.comboBoxChannel.InvokeRequired)
            {
                this.comboBoxChannel.Invoke(new System.Action(() =>
                {
                    if(this.channelIndex != this.comboBoxChannel.SelectedIndex)
                    {
                        OscChart.Series.Remove(OscChart.Series[0]);
                        OscChart.AxisX.Remove(OscChart.AxisX[0]);
                        OscChart.AxisY.Remove(OscChart.AxisY[0]);
                        this.channelIndex = this.comboBoxChannel.SelectedIndex;
                        InitChartConfig(channelIndex);
                    }
                }));
            }
            else
            {
                // 直接进行操作，因为当前线程就是UI线程
                if (this.channelIndex != this.comboBoxChannel.SelectedIndex)
                {
                    OscChart.Series.Remove(OscChart.Series[0]);
                    OscChart.AxisX.Remove(OscChart.AxisX[0]);
                    OscChart.AxisY.Remove(OscChart.AxisY[0]);
                    this.channelIndex = this.comboBoxChannel.SelectedIndex;
                    InitChartConfig(channelIndex);
                }
            } 
        }

        private void tbElasticModulus_TextChanged(object sender, EventArgs e)
        {
            if (tbElasticModulus1.Text.Length > 0)
            {
                this.EM1 = Convert.ToDouble(tbElasticModulus1.Text);
            }
        }

        private void tbAxialForce1_TextChanged(object sender, EventArgs e)
        {
            if (tbAxialForce1.Text.Length > 0)
            {
                this.AF1 = Convert.ToDouble(tbAxialForce1.Text);
            }
        }

        private void tbElasticModulus2_TextChanged(object sender, EventArgs e)
        {
            if (tbElasticModulus2.Text.Length > 0)
            {
                this.EM2 = Convert.ToDouble(tbElasticModulus2.Text);
            }
        }

        private void tbAxialForce2_TextChanged(object sender, EventArgs e)
        {
            if (tbAxialForce2.Text.Length > 0)
            {
                this.AF2 = Convert.ToDouble(tbAxialForce2.Text);
            }
        }

        public static decimal DivideAndRound(decimal dividend, decimal divisor, int decimalPlaces)
        {
            if (divisor == 0)
            {
                throw new DivideByZeroException();
            }
            decimal result = dividend / divisor;
            return Math.Round(result, decimalPlaces);
        }

        private void cbAlarmNotify_CheckedChanged(object sender, EventArgs e)
        {
            this.isAlarmNotify = this.cbAlarmNotify.Checked;
            this.tbAxialForceExceeds.Enabled = this.isAlarmNotify;
            this.tbRepeatTimes.Enabled = this.isAlarmNotify;
            this.cbAlarmEmail.Enabled = this.isAlarmNotify;
            this.tbAlarmEmail.Enabled = this.isAlarmNotify;
            this.cbAlarmPhone.Enabled = this.isAlarmNotify;
            this.tbAlarmPhone.Enabled = this.isAlarmNotify;
            
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            AppSettingsSection app = config.AppSettings;
            app.Settings["cbAlarmNotify"].Value = Convert.ToString(this.isAlarmNotify);
            config.Save(ConfigurationSaveMode.Modified);
        }

        private void tbAxialForceExceeds_TextChanged(object sender, EventArgs e)
        {
            if ((this.isAlarmNotify) && (this.tbAxialForceExceeds.Text.Length > 0))
            {
                this.axialForceExceeds = Convert.ToDouble(this.tbAxialForceExceeds.Text);
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbAxialForceExceeds"].Value = Convert.ToString(this.axialForceExceeds);
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        private void tbRepeatTimes_TextChanged(object sender, EventArgs e)
        {
            if ((this.isAlarmNotify) && (this.tbRepeatTimes.Text.Length > 0))
            {
                this.repeatTimes = Convert.ToInt16(this.tbRepeatTimes.Text);
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbRepeatTimes"].Value = Convert.ToString(this.repeatTimes);
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        private void cbAlarmEmail_CheckedChanged(object sender, EventArgs e)
        {
            if (this.isAlarmNotify)
            {
                this.isAlarmEmail = this.cbAlarmEmail.Checked;
                this.tbAlarmEmail.Enabled = this.isAlarmEmail;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbAlarmEmail"].Value = Convert.ToString(this.isAlarmEmail);
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        private void tbAlarmEmail_TextChanged(object sender, EventArgs e)
        {
            if ((this.isAlarmNotify) && (this.isAlarmEmail) && (this.tbAlarmEmail.Text.Length > 0))
            {
                this.alarmEmail = this.tbAlarmEmail.Text;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbAlarmEmail"].Value = alarmEmail;
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        private void cbAlarmPhone_CheckedChanged(object sender, EventArgs e)
        {
            if (this.isAlarmNotify)
            {
                this.isAlarmPhone = this.cbAlarmPhone.Checked;
                this.tbAlarmPhone.Enabled = this.isAlarmPhone;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbAlarmPhone"].Value = Convert.ToString(this.isAlarmPhone);
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        private void tbAlarmPhone_TextChanged(object sender, EventArgs e)
        {
            if ((this.isAlarmNotify) && (this.isAlarmPhone) && (this.tbAlarmPhone.Text.Length > 0))
            {
                this.alarmPhone = this.tbAlarmPhone.Text;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbAlarmPhone"].Value = this.alarmPhone;
                config.Save(ConfigurationSaveMode.Modified);
            }
        }

        public static void SendEmail(string host, int port, string fromEmail, string password, string toEmail, string subject, string body)
        {
            try
            {
                // 创建邮件消息
                MailMessage mail = new MailMessage();
                mail.From = new MailAddress(fromEmail);
                mail.To.Add(toEmail);
                mail.Subject = subject;
                mail.Body = body;

                // 设置SMTP客户端
                using (SmtpClient client = new SmtpClient(host, port))
                {
                    client.Credentials = new NetworkCredential(fromEmail, password);
                    client.EnableSsl = true; // 如果SMTP服务器需要安全连接，则启用

                    // 发送邮件
                    client.Send(mail);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                
            }
        }


        public static void SendSms(string phoneNumber, string[] templateParamSet)
        {
            try
            {
                // 必要步骤：
                // 实例化一个认证对象，入参需要传入腾讯云账户密钥对 SecretId，SecretKey。
                // 为了保护密钥安全，建议将密钥设置在环境变量中或者配置文件中。
                // 硬编码密钥到代码中有可能随代码泄露而暴露，有安全隐患，并不推荐。
                // 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
                // SecretId、SecretKey 查询：https://console.cloud.tencent.com/cam/capi
                Credential cred = new Credential
                {
                    //SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"),
                    //SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY")
                    SecretId = "AKIDaRMzSimmfMqDWfcQ1ShkutiNrAIFzXuA",
                    SecretKey = "CAyc6lCQ2cLypW2OaeowtfJqCOemkXBg"
                };

                /* 非必要步骤:
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                ClientProfile clientProfile = new ClientProfile();
                /* SDK默认用TC3-HMAC-SHA256进行签名
                 * 非必要请不要修改这个字段 */
                clientProfile.SignMethod = ClientProfile.SIGN_TC3SHA256;
                /* 非必要步骤
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                HttpProfile httpProfile = new HttpProfile();
                /* SDK默认使用POST方法。
                 * 如果您一定要使用GET方法，可以在这里设置。GET方法无法处理一些较大的请求 */
                httpProfile.ReqMethod = "POST";
                httpProfile.Timeout = 10; // 请求连接超时时间，单位为秒(默认60秒)
                /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
                httpProfile.Endpoint = "sms.tencentcloudapi.com";
                // 代理服务器，当您的环境下有代理服务器时设定（无需要直接忽略）
                // httpProfile.WebProxy = Environment.GetEnvironmentVariable("HTTPS_PROXY");

                clientProfile.HttpProfile = httpProfile;
                /* 实例化要请求产品(以sms为例)的client对象
                 * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
                SmsClient client = new SmsClient(cred, "ap-nanjing", clientProfile);

                /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
                 * 您可以直接查询SDK源码确定SendSmsRequest有哪些属性可以设置
                 * 属性可能是基本类型，也可能引用了另一个数据结构
                 * 推荐使用IDE进行开发，可以方便的跳转查阅各个接口和数据结构的文档说明 */
                SendSmsRequest req = new SendSmsRequest();

                /* 基本类型的设置:
                 * SDK采用的是指针风格指定参数，即使对于基本类型您也需要用指针来对参数赋值。
                 * SDK提供对基本类型的指针引用封装函数
                 * 帮助链接：
                 * 短信控制台: https://console.cloud.tencent.com/smsv2
                 * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
                /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666 */
                // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
                req.SmsSdkAppId = "1400957364";

                /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名 */
                // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
                req.SignName = "屏优多科技";

                /* 模板 ID: 必须填写已审核通过的模板 ID */
                // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
                req.TemplateId = "2342074";

                /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，若无模板参数，则设置为空 */
                req.TemplateParamSet = templateParamSet;

                /* 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]
                 * 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号*/
                req.PhoneNumberSet = new String[] { "+86" + phoneNumber };

                /* 用户的 session 内容（无需要可忽略）: 可以携带用户侧 ID 等上下文信息，server 会原样返回 */
                req.SessionContext = "告警";

                /* 短信码号扩展号（无需要可忽略）: 默认未开通，如需开通请联系 [腾讯云短信小助手] */
                req.ExtendCode = "";

                /* 国内短信无需填写该项；国际/港澳台短信已申请独立 SenderId 需要填写该字段，默认使用公共 SenderId，无需填写该字段。注：月度使用量达到指定量级可申请独立 SenderId 使用，详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
                req.SenderId = "";

                SendSmsResponse resp = client.SendSmsSync(req);

                // 输出json格式的字符串回包
                Console.WriteLine(AbstractModel.ToJsonString(resp));

                /* 当出现以下错误码时，快速解决方案参考
                 * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * 更多错误，可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
                 */
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            Console.Read();
        }

        public static void PullSmsSendStatus()
        {
            try
            {
                // 必要步骤：
                // 实例化一个认证对象，入参需要传入腾讯云账户密钥对 SecretId，SecretKey。
                // 为了保护密钥安全，建议将密钥设置在环境变量中或者配置文件中。
                // 硬编码密钥到代码中有可能随代码泄露而暴露，有安全隐患，并不推荐。
                // 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
                // SecretId、SecretKey 查询：https://console.cloud.tencent.com/cam/capi
                Credential cred = new Credential
                {
                    //SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"),
                    //SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY")
                    SecretId = "AKIDaRMzSimmfMqDWfcQ1ShkutiNrAIFzXuA",
                    SecretKey = "CAyc6lCQ2cLypW2OaeowtfJqCOemkXBg"
                };
                /* 非必要步骤:
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                ClientProfile clientProfile = new ClientProfile();
                /* SDK 默认用 TC3-HMAC-SHA256 进行签名
                 * 非必要请不要修改该字段 */
                clientProfile.SignMethod = ClientProfile.SIGN_TC3SHA256;
                /* 非必要步骤
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                HttpProfile httpProfile = new HttpProfile();
                /* SDK 默认使用 POST 方法
                 * 如需使用 GET 方法，可以在此处设置，但 GET 方法无法处理较大的请求 */
                httpProfile.ReqMethod = "POST";
                httpProfile.Timeout = 30; // 请求连接超时时间，单位为秒(默认60秒)
                /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
                httpProfile.Endpoint = "sms.tencentcloudapi.com";
                // 代理服务器，当您的环境下有代理服务器时设定
                // httpProfile.WebProxy = Environment.GetEnvironmentVariable("HTTPS_PROXY");
                clientProfile.HttpProfile = httpProfile;
                /* 实例化 SMS 的 client 对象
                 * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
                SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);
                /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
                 * 您可以直接查询 SDK 源码确定 SendSmsRequest 有哪些属性可以设置
                 * 属性可能是基本类型，也可能引用了另一个数据结构
                 * 推荐使用 IDE 进行开发，可以方便地跳转查阅各个接口和数据结构的文档说明 */
                PullSmsSendStatusRequest req = new PullSmsSendStatusRequest();

                /* 基本类型的设置:
                 * SDK 采用的是指针风格指定参数，即使对于基本类型也需要用指针来对参数赋值
                 * SDK 提供对基本类型的指针引用封装函数
                 * 帮助链接：
                 * 短信控制台：https://console.cloud.tencent.com/smsv2
                 * 腾讯云短信小助手：https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */

                // 设置拉取最大条数，最多100条
                req.Limit = 100;
                /* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SdkAppId，例如1400006666 */
                req.SmsSdkAppId = "1400957364";
                // 通过 client 对象调用 PullSmsSendStatus 方法发起请求，注意请求方法名与请求对象是对应的
                // 返回的 resp 是一个 PullSmsSendStatusResponse 类的实例，与请求对象对应
                PullSmsSendStatusResponse resp = client.PullSmsSendStatusSync(req);
                // 输出 JSON 格式的字符串回包
                Console.WriteLine(AbstractModel.ToJsonString(resp));
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            Console.Read();
        }

    }

}

